#include "swagme.h"
#include "config.h"
#include "Game.h"
#include "Display.h"
#include "Player.h"
#include "Npc.h"
#include "Scene.h"
#include "Swag.h"
#include "sound.h"

#include <iostream>
#include <algorithm>
using std::cout;
using std::cerr;
using std::endl;

// 2 minutes for each screen (for now)
#define TIME_LEFT (2*60)

Game::Game() : 
    mTimeLeftInSec(TIME_LEFT), 
    mStartTimeInMs(SDL_GetTicks()), 
    mpDisplay(new Display()),
    mpPlayer(NULL),
    mpConfig(InitConfig("resources/wad.xml")),
    mpCurLevel(NULL),
    mbResortRenderables(true),
    sound(NULL),
    mbUpKeyPressed(false),
    mbRightKeyPressed(false),
    mbDownKeyPressed(false),
    mbLeftKeyPressed(false)
{
  if(!mpDisplay) {
    cerr << "Error!  Could not initialize Display" << endl;
    exit(4);
  }
  cout << "Game and Display initialized." << endl;
  Init();
}

Game::~Game()
{
  // release the audio device
  if (sound) delete sound;
  
  // wipe this collection clean
  mRenderables.clear();

  if (mColMap.map) delete[] mColMap.map;

  for (int idx = 0; idx < mSwaggage.size(); idx++) {
    delete mSwaggage[idx];
  }
  mSwaggage.clear();

  for (int idx = 0; idx < mNonplayers.size(); idx++) {
    delete mNonplayers[idx];
  }
  mNonplayers.clear();

  for( map<string,Sprite*>::iterator spriteIt = mSprites.begin(); spriteIt != mSprites.end(); ++spriteIt) {
    delete spriteIt->second;
  }
  mSprites.clear();

  if(mpPlayer) {
    delete mpPlayer;
    mpPlayer = NULL;
  }

  if(mpConfig) {
    delete mpConfig;
    mpConfig = NULL;
  }

  if(mpScene) {
    delete mpScene;
    mpScene = NULL;
  }

  if(mpDisplay) {
    delete mpDisplay;
    mpDisplay = NULL;
  }

  cout << "Game and Display destroyed." << endl;
}

// read in all resources
// find the first conference
// set up player position
void Game::Init()
{
  InitSprites();
  InitSounds();
  
  // get all levels (order matters in the XML doc)
  int numLevels = mpConfig->getElementsByTagName("conference", &mAllLevels);
  if(!numLevels) {
    cerr << "Error!  Could not find any <conference> elements in wad.xml" << endl;
    exit(2000);
  }

  // find the first conference
  mpCurLevel = mAllLevels[0];
  if(!mpCurLevel) {
    cerr << "Error!  No suitable levels found (check order attributes on <conference> elements)" << endl;
    exit(-1);
  }
  int lev_width = ATOI(mpCurLevel->attr["width"]);
  int lev_height = ATOI(mpCurLevel->attr["height"]);
  cout << "First level was '" << mpCurLevel->attr["name"] << "'" << " (" << lev_width << "," << lev_height << ")" << endl;

  // set up background map
  mpScene = new Scene(mpCurLevel->attr["background"].c_str(), mpCurLevel->attr["collisionMap"].c_str(), mpCurLevel->attr["floor"].c_str());
  if(!mpScene) {
    cerr << "Error!  Could not initialize scene" << endl;
    exit(5);
  }

  // InitScene() initializes the colmap
  InitScene();
  
  // read in swag from <conference> element
  NodeArray swagNodes;
  mpCurLevel->getElementsByTagName("swag", &swagNodes);
  for(NodeIter swagIt = swagNodes.begin(); swagIt != swagNodes.end(); ++swagIt) {
    XMLNode* node = *swagIt;
    cout << "Found <swag> with type=" << node->attr["type"] << endl;

    int pts = 10;
    if(node->attr["type"] == "PEN") { pts = 8; }
    else if(node->attr["type"] == "HAT") { pts = 12; }
    else if(node->attr["type"] == "TSHIRT") { pts = 20; }
    else {
      cerr << "Warning!  Ignoring unknown swag type '" << node->attr["type"] << "'" << endl;
      continue;
    }

    if(mSprites.find(node->attr["sprite"]) == mSprites.end()) {
      cerr << "Error!  Could not find sprite '" << mSprites[node->attr["sprite"]] << "'" << endl;
      exit(-1);
    }

    int x = 0;
    int y = 0;
    bool bFoundLocation = false;
    while(!bFoundLocation) {
      x = ATOI(node->attr["x"]);
      y = ATOI(node->attr["y"]);
      if(node->attr["x"] == "RANDOM") {
        x = random(30, lev_width-30);
      }
      if(node->attr["y"] == "RANDOM") {
        y = random(30, lev_width-30);
      }

      if( node->attr["x"] == "RANDOM" || node->attr["y"] == "RANDOM") {
        // TODO:  Swag is hard-coded to 12x12, we need to change this 
        // code here if it ever is not so.  This means that we have to
        // check 4 colmap indices (swag takes up a 2x2 square in colmap)
        int colx = x/mColMap.cellInPixels;
        int coly = y/mColMap.cellInPixels;
        int index = coly*mColMap.width + colx;
        if(!mColMap.map[index] && !mColMap.map[index+1] && 
          !mColMap.map[index+mColMap.width] && !mColMap.map[index+mColMap.width+1]) 
        {
          bFoundLocation = true;
        }
      }
      else {
        bFoundLocation = true;
      }

    } // while(!bFoundLocation)

    Swag* aSwag = new Swag(mSprites[node->attr["sprite"]], x, y, pts );
    mSwaggage.push_back( aSwag ); // owned
    mRenderables.push_back( aSwag ); // not owned
    mColMap.addTouchableObject( aSwag ); // not owned

  }

  // read in npcs from <conference> element
  NodeArray npcNodes;
  mpCurLevel->getElementsByTagName("npc", &npcNodes);
  for(NodeIter npcIt = npcNodes.begin(); npcIt != npcNodes.end(); ++npcIt) {
    XMLNode* node = *npcIt;
    cout << "Found <npc> with type=" << node->attr["type"] << endl;

    if( (node->attr["type"] != "BABE") && (node->attr["type"] != "NERD") ) {
      cerr << "Warning!  Ignoring unknown NPC type '" << node->attr["type"] << "'" << endl;
      continue;
    }

    if(mSprites.find(node->attr["sprite"]) == mSprites.end()) {
      cerr << "Error!  Could not find sprite '" << mSprites[node->attr["sprite"]] << "'" << endl;
      exit(-1);
    }

    int x = 0;
    int y = 0;
    bool bFoundLocation = false;
    while(!bFoundLocation) {
      x = ATOI(node->attr["x"]);
      y = ATOI(node->attr["y"]);
      if(node->attr["x"] == "RANDOM") {
        x = random(30, lev_width-30);
      }
      if(node->attr["y"] == "RANDOM") {
        y = random(30, lev_width-30);
      }

      if(node->attr["y"] == "RANDOM" || node->attr["y"] == "RANDOM") {
        // TODO:  NPCs are hard-coded to 12x12, we need to change this 
        // code here if it ever is not so.  This means that we have to
        // check 4 colmap indices (npc takes up a 2x2 square in colmap)
        int colx = x/mColMap.cellInPixels;
        int coly = y/mColMap.cellInPixels;
        int index = coly*mColMap.width + colx;
        if(!mColMap.map[index] && !mColMap.map[index+1] && 
          !mColMap.map[index+mColMap.width] && !mColMap.map[index+mColMap.width+1]) 
        {
          bFoundLocation = true;
        }
      }
      else {
        bFoundLocation = true;
      }
    } // while(!bFoundLocation)

    sm_direction npcFacing;
    if(node->attr["facing"] == "RANDOM") {
      npcFacing = (sm_direction)(random(MIN_DIR, MAX_DIR));
    }
    else {
      int tempFacing = ATOI(node->attr["facing"]);
      if(tempFacing < MIN_DIR || tempFacing > MAX_DIR) {
        cerr << "Warning!  XML file had invalid value for NPC facing: " << npcFacing << endl;
        tempFacing = sm_dir_down;
      }
      npcFacing = (sm_direction)(tempFacing);
    }

    Npc* aNpc = new Npc(mSprites[node->attr["sprite"]], x, y);
    aNpc->SetFacing( npcFacing );
    mNonplayers.push_back( aNpc ); // owned
    mRenderables.push_back( aNpc ); // not owned
    mColMap.addTouchableObject( aNpc ); // not owned
  }

  // initialize player
  // - get initial player position
  XMLNode* ps = mpCurLevel->getElementByTagName("playerStart");
  if(!ps ) {
    cerr << "Error!  Could not find <playerStart> element in first <conference> element" << endl;
    exit(-1);
  }
  // - get player sprite
  if(mSprites.find("player-sprite") == mSprites.end())  { 
    cerr << "Error!  Could not find player-sprite <sprite> in XML" << endl; 
    exit(-1); 
  }
  // - create player
  mpPlayer = new Player(mSprites["player-sprite"],
                  ATOI(ps->attr["x"]),
                  ATOI(ps->attr["y"]));
  int playerFacing = ATOI(ps->attr["facing"]);
  if(playerFacing < MIN_DIR || playerFacing > MAX_DIR) { 
    cout << "WARNING:  XML file had an invalid value for playerStart facing: " << playerFacing << endl;
    playerFacing = sm_dir_down;
  }
  mpPlayer->SetFacing( (sm_direction)playerFacing );
  mRenderables.push_back(mpPlayer); // not owned
  mColMap.addTouchableObject( mpPlayer ); // not owned

  dumpColMap();
}

void Game::InitSprites()
{
  // get the resources element
  XMLNode* pResources = mpConfig->getElementByTagName( "resources" );
  if(!pResources) { return; }

  // set up sprite resources
  vector<XMLNode*> spriteNodes;
  int numSprites = pResources->getElementsByTagName("sprite", &spriteNodes);
  if(!numSprites) {
    cerr << "Error!  Could not find any <sprite> elements in wad.xml" << endl;
    exit(2004);
  }
  for(NodeIter spriteIt = spriteNodes.begin(); spriteIt != spriteNodes.end(); ++spriteIt) {
    XMLNode* node = *spriteIt;
    Sprite* sprite;

    if(node->id().length() == 0) { cerr << "Error!  Sprite id is blank" << endl; exit(2005); }
    if(node->attr["src"].length() == 0) { cerr << "Error!  Sprite '" << node->id() << "' does not have a src attribute" << endl; exit(2006); }
    if(node->attr["shadow"].length() == 0) {
      sprite = new Sprite(node->attr["src"].c_str());
    } else {
      sprite = new Sprite(node->attr["src"].c_str(), node->attr["shadow"].c_str());
    }
    mSprites[node->id()] = sprite;
  }
}

void Game::InitSounds()
{
  // get the resources element
  XMLNode* pResources = mpConfig->getElementByTagName( "resources" );
  if(!pResources) { return; }

  sound = new Sound();
  vector<XMLNode*> soundNodes;
  int numSounds = pResources->getElementsByTagName("sound", &soundNodes);
  for(NodeIter soundIt = soundNodes.begin(); soundIt != soundNodes.end(); ++soundIt) {
    XMLNode* node = *soundIt;
    // Sound* sound;

    if(node->id().length() == 0) { cerr << "Error!  Sound id is blank" << endl; exit(2007); }
    if(node->attr["src"].length() == 0) { cerr << "Error!  Sound '" << node->id() << "' does not have a src attribute" << endl; exit(2008); }
    sound->Load(node->attr["src"].c_str(),node->attr["id"]);
    // sound->Play(node->attr["id"]);
  }
}

void Game::InitScene()
{
  mpScene->InitScene();
  cout << "bb" << endl;  

  mColMap.cellInPixels = 8; // each square represents an 8x8 pixel region
  mColMap.width = ATOI(mpCurLevel->attr["width"])/mColMap.cellInPixels;
  mColMap.height = ATOI(mpCurLevel->attr["height"])/mColMap.cellInPixels;
  cout << "cc" << endl;  

  crunchCollisionMap(mpScene->CollisionMap);
}

/*
 * Return the pixel value at (x, y)
 * NOTE: The surface must be locked before calling this!
 * (from the SDL docs at file:///usr/share/doc/packages/SDL-devel/html/guidevideo.html#AEN90)
 */
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
  int bpp = surface->format->BytesPerPixel;
  /* Here p is the address to the pixel we want to retrieve */
  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      return *p;

    case 2:
      return *(Uint16 *)p;

    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
        return p[0] << 16 | p[1] << 8 | p[2];
      else
        return p[0] | p[1] << 8 | p[2] << 16;

    case 4:
      return *(Uint32 *)p;

    default:
      return 0;       /* shouldn't happen, but avoids warnings */
  }
}

int Game::crunchCollisionMap(SDL_Surface *s) {
  int size = mColMap.width * mColMap.height;
  mColMap.map = new TouchableObject*[size];
  cout << "Crunching: " << hex;
  for (int ix=0, iy=0; (ix<s->w) && (iy<s->h); ix++,iy++) {
    cout << getpixel(s,ix, iy) << " ";
  }

  // ensure that s->w and s->h correspond to the same size as mColMap.map
  if ((mColMap.width * mColMap.cellInPixels != s->w) || (mColMap.height * mColMap.cellInPixels != s->h)) {
    cout << "Error: mColMap dimensions don't match image dimensions mColMap.width:" << mColMap.width << " s->w: " << s->w << " mColMap.height: " << mColMap.height << " s->h: " << s->h << endl;
    exit(-1);
  }

  // We read the collisionMap one cell at a time. So we start at (0,0) then read all pixels up to (8,8). This determines one entry in mColMap.map. After that we move on to the cell at (8,0) to (16,8)
  for (int cx=0; cx < mColMap.width; cx++) {
    for (int cy=0; cy < mColMap.height; cy++) {
      int pixelCount=0;
      // read in one cell from the full scale map
      for (int ix=0; ix < mColMap.cellInPixels; ix++) {
        for (int iy=0; iy < mColMap.cellInPixels; iy++) {
          // TODO: this is just the colour that the Gimp chose for transparent in this image but I think there's an alpha channel that could be used instead
          // get the current pixel
          if (getpixel(s, (cx * mColMap.cellInPixels) + ix,  (cy * mColMap.cellInPixels) + iy)== 0) {
          } else {
            pixelCount++;
          }
        }
      }
      // compute the map value from the full scale map
      if (pixelCount < 32) {
        mColMap.map[cx + cy * mColMap.width]=NULL;
      } else {
        mColMap.map[cx + cy * mColMap.width]=&theWall;
      }
    }
  }
  cout << dec << endl;
}

void Game::dumpColMap() {
  cout << "Collision Map: " << endl;
  for (int iy=0; iy < mColMap.height; iy++) {
    for (int ix=0; ix < mColMap.width; ix++) {
      TouchableObject* obj = mColMap.map[ix + iy * mColMap.width];
      if (obj) {
        sm_cd_object type = obj->getObjectType();
        switch(type) {
          case sm_cdo_wall: cout << "X"; break;
          case sm_cdo_player: cout << "P"; break;
          case sm_cdo_npc: cout << "N"; break;
          case sm_cdo_swag: cout << "S"; break;
        } // switch(type)
      } else {
        cout << " ";
      }
    }
    cout << endl;
  }
}

// primarily what this is does is update the time left
// and display to the status area
void Game::Update(Uint8* keystate)
{
  if(IsGameOver()) { return; }

  if(mTimeLeftInSec > 0) {
    int oldTime = mTimeLeftInSec;
    mTimeLeftInSec = TIME_LEFT - (SDL_GetTicks() - mStartTimeInMs)/1000;
    if(mTimeLeftInSec != oldTime) {
//    cout << "Time Left (s) = " << mTimeLeftInSec << " seconds." << endl;
    }
  }

  // keyboard controls:
  // directions:
  //  w,a,d,s or up, left, down, right arrows or num pad equiv
//  static sm_direction mpPlayer->Facing = sm_dir_none;
  // if we want the player to just keep moving until they change direction, then this can be set to true or removed
  // I kind of like that - it's more like pacman
  bool moveKeyPressed=false; 
  
  // save the previous position of the player
  mpPlayer->update();

  if (keystate[SDLK_w] || keystate[SDLK_UP] || keystate[SDLK_KP8]) {
    // every time the up key is pressed, add a keypress to the stack
    if(!mbUpKeyPressed) { 
      // add an "up" key press to the stack
      mKeyPressStack.push_front(sm_dir_up);
      mbUpKeyPressed = true;
    }
  } 
  else { 
    // every time the up key is released, remove the keypress from the stack
    if(mbUpKeyPressed) {
      // remove any "up" key presses from the stack
      for(KeyIter it = mKeyPressStack.begin(); it != mKeyPressStack.end(); ++it) {
        if(*it == sm_dir_up) { mKeyPressStack.erase(it); break;}
      }
      mbUpKeyPressed = false;
    }
  }

  if (keystate[SDLK_a] || keystate[SDLK_LEFT] || keystate[SDLK_KP4]) {
    if(!mbLeftKeyPressed) { 
      mKeyPressStack.push_front(sm_dir_left);
      mbLeftKeyPressed = true;
    }
  } 
  else { 
    if(mbLeftKeyPressed) {
      for(KeyIter it = mKeyPressStack.begin(); it != mKeyPressStack.end(); ++it) {
        if(*it == sm_dir_left) { mKeyPressStack.erase(it); break;}
      }
      mbLeftKeyPressed = false;
    }
  }

  if (keystate[SDLK_s] || keystate[SDLK_DOWN] || keystate[SDLK_KP2]) {
    if(!mbDownKeyPressed) { 
      mKeyPressStack.push_front(sm_dir_down);
      mbDownKeyPressed = true;
    }
  } 
  else { 
    if(mbDownKeyPressed) {
      for(KeyIter it = mKeyPressStack.begin(); it != mKeyPressStack.end(); ++it) {
        if(*it == sm_dir_down) { mKeyPressStack.erase(it); break;}
      }
      mbDownKeyPressed = false;
    }
  }

  if (keystate[SDLK_d] || keystate[SDLK_RIGHT] || keystate[SDLK_KP6]) {
    if(!mbRightKeyPressed) { 
      mKeyPressStack.push_front(sm_dir_right);
      mbRightKeyPressed = true;
    }
  }
  else { 
    if(mbRightKeyPressed) {
      for(KeyIter it = mKeyPressStack.begin(); it != mKeyPressStack.end(); ++it) {
        if(*it == sm_dir_right) { mKeyPressStack.erase(it); break;}
      }
      mbRightKeyPressed = false;
    }
  }

  // set our facing to the top-most key-press in our stack
  if(mKeyPressStack.size() > 0) {
    mpPlayer->SetFacing(mKeyPressStack.front());
    moveKeyPressed = true;
  }

  // set up SDL_Rects for use with CDR
  CollisionEvent event;
  SDL_Rect curPos, newPos;

  if (moveKeyPressed) {

    curPos = mpPlayer->getBox();

    // new Pos is adjusted by facing and speed
    newPos.x = curPos.x; newPos.y = curPos.y;
    if(mpPlayer->GetFacing() == sm_dir_up) { newPos.y -= mpPlayer->speed; }
    if(mpPlayer->GetFacing() == sm_dir_left) { newPos.x -= mpPlayer->speed; }
    if(mpPlayer->GetFacing() == sm_dir_down) { newPos.y += mpPlayer->speed; }
    if(mpPlayer->GetFacing() == sm_dir_right) { newPos.x += mpPlayer->speed; }

    mColMap.removeTouchableObject( mpPlayer );
    moveRectWithCdr( mColMap, curPos, newPos, event );

    // first do cdr for swag
    if(event.result == sm_cdr_partial_move || event.result == sm_cdr_cannot_move) {
      if(event.cdrObjectType == sm_cdo_swag) {
        Swag* pSwag = dynamic_cast<Swag*>(event.cdrObject);
        mpPlayer->Points += pSwag->getPoints();
        // NOTE: remove it from mRenderables before we delete it so we don't get a segfault!
        mRenderables.erase(std::find(mRenderables.begin(), mRenderables.end(), pSwag));
        // also, remove it from the col map
        mColMap.removeTouchableObject( pSwag );
        mSwaggage.erase(std::find(mSwaggage.begin(), mSwaggage.end(), pSwag));
        delete pSwag;
        sound->Play("getswag");

        if(mSwaggage.size() == 0) {
          while(mTimeLeftInSec > 0) {
            mpPlayer->Points += 5;
            --mTimeLeftInSec;
            DrawScreen();
          }
        }
      }
    }

    if(event.result == sm_cdr_success || event.result == sm_cdr_partial_move) {
      //cout << "Was able to move, adjusting player now" << endl;
      if (!sound->IsPlaying("walk")) sound->Play("walk");
      mpPlayer->move(event.finalX, event.finalY);
      mbResortRenderables = true;
    }
    else if(event.result == sm_cdr_failure) {
      cerr << "CDR failure" << endl;
      mbResortRenderables = true;
    }
    else { // could not move
      sound->Stop("walk");
//      cout << "BUMP" << endl;
    }
    mColMap.addTouchableObject( mpPlayer );

  } //  if (moveKeyPressed)
  else {
    if (sound->IsPlaying("walk")) sound->Stop("walk");
  }

  // now let npcs walk around...
  for(NpcIter it = mNonplayers.begin(); it != mNonplayers.end(); ++it) {
    Npc* npc = *it;

    curPos = npc->getBox();
    newPos.x = curPos.x; newPos.y = curPos.y;
    if(npc->GetFacing() == sm_dir_up) { newPos.y -= npc->speed; }
    if(npc->GetFacing() == sm_dir_left) { newPos.x -= npc->speed; }
    if(npc->GetFacing() == sm_dir_down) { newPos.y += npc->speed; }
    if(npc->GetFacing() == sm_dir_right) { newPos.x += npc->speed; }

    mColMap.removeTouchableObject( npc );
    moveRectWithCdr( mColMap, curPos, newPos, event );

    if(event.result == sm_cdr_success || event.result == sm_cdr_partial_move) {
      npc->move(event.finalX, event.finalY);
      mbResortRenderables = true;
    }
    else {
      // pick a random number between 1 and 4
      npc->SetFacing( (sm_direction)random(1,4) );
    }

    mColMap.addTouchableObject( npc );
  } // for each NPC

//  dumpColMap();

  DrawScreen();
}

class TouchableObjectDepthComparison
{
    public:
        bool operator()(TouchableObject* const &first,
                        TouchableObject* const &second) const
        {
            // static long c;
            // cout << "c " << c++;
            return (first->getY() < second->getY());
        }
};

void Game::DrawScreen()
{
  // sort all renderable objects by y value so they will draw back to front
  // only sort after any onscreen object moves
  if (mbResortRenderables) {
    // cout << "sort " << mRenderables.size() << endl;
    sort(mRenderables.begin(),mRenderables.end(),TouchableObjectDepthComparison());
    // cout << endl;
    mbResortRenderables = false;
  }

  
  mpDisplay->LockScreen();

  // commit to screen
  // set up to redraw
  DrawScene();
  // draw the floor
  DrawFloor();

  // draw shadows for all TouchableObjects
  for (int idx = 0; idx < mRenderables.size(); ++idx) {
    mRenderables[idx]->Draw(mpDisplay->getScreen(), mRenderables[idx]->getSpeed(), true);
  }

  // draw the map
  DrawBackground();

  // draw all TouchableObjects
  for (int idx = 0; idx < mRenderables.size(); ++idx) {
    mRenderables[idx]->Draw(mpDisplay->getScreen(), mRenderables[idx]->getSpeed(), false);
  }

  // TODO: only update when i need to
  DrawStatus();

  // flip the screen now that all rendering is complete 
  mpDisplay->UnlockScreen();
  mpDisplay->Flip();
}

void Game::DrawScene()
{
  // blit in layers,
  //  1. background
  //  2. map and status graphics
  //  3. sprites

  // temporary background - two rectangles against black for now 
  SDL_Surface* scr = mpDisplay->getScreen();
  SDL_Rect rcMap = { MAP_POS_X, MAP_POS_Y, MAP_SIZE_X, MAP_SIZE_Y };
  SDL_Rect rcStatus = { STATUS_POS_X, STATUS_POS_Y, STATUS_SIZE_X, STATUS_SIZE_Y };
  SDL_FillRect(scr, NULL, SDL_MapRGB(scr->format, 0, 0, 0)); // black
  SDL_FillRect(scr, &rcStatus, SDL_MapRGB(scr->format, 192, 255, 192)); // lt green
}

void Game::DrawBackground()
{
  if(mpScene->Background) {
    SDL_BlitSurface(mpScene->Background, NULL, mpDisplay->getScreen(), &mpScene->MapRect);
  }
}

void Game::DrawFloor()
{
  if(mpScene->Floor) {
    SDL_BlitSurface(mpScene->Floor, NULL, mpDisplay->getScreen(), &mpScene->MapRect);
  }
}

void Game::DrawStatus()
{
  if(!mpDisplay || !mpPlayer) { return; }

  char szTimeStr[30];
  int time_x = STATUS_POS_X + 20; int time_y = STATUS_POS_Y + 50;
  SDL_Color black = {0,0,0,255}; 
  SDL_Color time_color = {0,0,0,255}; // black by default
  if(mTimeLeftInSec > 0) {
    sprintf(szTimeStr, "Time Left: %d:%02d", mTimeLeftInSec/60, mTimeLeftInSec%60);
  }
  else {
    sprintf(szTimeStr, "Game over, chump!");
    time_color.r = 255; // red
  }

  char szPlayerStr[30];
  int player_x = STATUS_POS_X + 10; int player_y = STATUS_POS_Y + 120;
  sprintf(szPlayerStr, "Pos: (%d,%d)", mpPlayer->getLeft(), mpPlayer->getTop());

  char szPlayerScore[20];
  int player_points_y = player_y + 20;
  sprintf(szPlayerScore, "Points: %d", mpPlayer->Points);

  char szLocation[40];
  sprintf(szLocation, "(%s)", mpCurLevel->attr["location"].c_str());

  int cx = STATUS_POS_X + (STATUS_SIZE_X/2);

  // rendering calls go here
  mpDisplay->SetCurrentFont("resources/fonts/VeraBd.ttf", 14);
  mpDisplay->DrawText(mpCurLevel->attr["name"].c_str(), black, 
                      cx, STATUS_POS_Y + 5, sm_disp_centerh);
  mpDisplay->SetCurrentFont("resources/fonts/Vera.ttf", 12);
  mpDisplay->DrawText(szLocation, black, 
                      cx, STATUS_POS_Y + 25, sm_disp_centerh);

  mpDisplay->SetCurrentFont("resources/fonts/Vera.ttf", 14);
  mpDisplay->DrawText(szTimeStr, time_color, cx, time_y, sm_disp_centerh);
  mpDisplay->DrawText(szPlayerStr, time_color, player_x, player_y);
  mpDisplay->DrawText(szPlayerScore, time_color, player_x, player_points_y);
}
